package com.demo.java.reference;

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 参考: https://blog.csdn.net/qq_38245668/article/details/105891161
 *
 * 这种锁针对具有相同key需要加锁的业务, 以提高并发性能
 */
public class WeakRefHashLock {
    // 存储key与对应锁的引用
    private Map<Object, LockWeakReference> lockMap = new ConcurrentHashMap<>();
    // 过期锁的引用
    private ReferenceQueue<ReentrantLock> queue = new ReferenceQueue<>();

    public Lock getLock(Object key) {
        if (lockMap.size() > 1000) {
            clearEmptyRef();
        }
        LockWeakReference lockWeakReference = lockMap.get(key);
        Lock lock = null;
        // // 获取弱引用指向的lock，这里如果获取到 lock 对象值，将会使 lock 对象值的弱引用提升为强引用，不会被gc回收
        while ((lock = lockWeakReference == null ? null : lockWeakReference.get()) == null) {
            // 获取不到 lock，移除map中无用的ref
            clearEmptyRef();
            // 使用 putIfAbsent，在多线程环境下，针对同一 key ，weakReference 均指向同一弱引用对象
            // 这里使用 可重入锁
            lockWeakReference = lockMap.putIfAbsent(key, new LockWeakReference(key, new ReentrantLock(), queue));
        }
        // 在 putIfAbsent 的执行和 weakReference.get() 执行的间隙，可能存在执行gc的过程，会导致 lock 为null，所以使用while循环获取
        return lock;
    }

    public int lockSize() {
        return lockMap.size();
    }

    public void clearEmptyRef() {
        Reference<? extends ReentrantLock> ref;
        while ((ref = queue.poll()) != null) {
            LockWeakReference reference = (LockWeakReference) ref;
            lockMap.remove(reference.getKey());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        WeakRefHashLock hashLock = new WeakRefHashLock();
        for (int i = 0; i < 100000; i++) {
            Lock lock = hashLock.getLock(i + "");
            lock.lock();
            System.gc();
            hashLock.clearEmptyRef();
            Thread.sleep(10000);
            System.out.println("loop: " + i + ", lockSize: " + hashLock.lockSize());
            lock.unlock();
        }

        System.out.println("end, lockSize: " + hashLock.lockSize());
        System.gc();
        Thread.sleep(100L);
        hashLock.clearEmptyRef();
        System.out.println("end1, lockSize: " + hashLock.lockSize());
    }

    static class LockWeakReference extends WeakReference<ReentrantLock> {
        private Object key;
        public LockWeakReference(Object key, ReentrantLock lock, ReferenceQueue<? super ReentrantLock> q) {
            super(lock, q);
            this.key = key;
        }

        public Object getKey() {
            return key;
        }

        public void setKey(Object key) {
            this.key = key;
        }
    }
}
